<?php

/**
 * uniBoard Template compiler
 *
 * Light and fast forum solution for web 2.0-like communities.
 *
 * @package		uniBoard
 * @author		Rafał Pitoń
 * @license		http:// www.gnu.org/licenses/gpl-3.0.html
 * @since		Version 0.1
 * 
 * ------------------------------------------------------------------------
 * 
 * $Date: 2010-09-20 21:23:27 +0000 (Mon, 20 Sep 2010) $
 * $Revision: 64 $
 * $Author: rafio.xudb@gmail.com $
 */

// ------------------------------------------------------------------------

class class_template_compiler{
	
	/**
	 * Main class pointer
	 *
	 * @var object
	 */
	
	private $appCore				= NULL;
	
	/**
	 * Current templates group
	 *
	 * @var string
	 */
	
	private $templates_group 		= '';
	
	/**
	 * Actual template
	 *
	 * @var string
	 */
	
	private $actual_template 		= '';
	
	/**
	 * Raw templates
	 *
	 * @var array
	 */
	
	private $raw_templates 			= array();
	
	/**
	 * Drawed html
	 *
	 * @var string
	 */
	
	private $drawed_html			= '';
	
	/**
	 * Parsed condition
	 *
	 * @var string
	 */
	
	private $parsed_condition 		= '';
	
	/**
	 * Parsed bit
	 *
	 * @var string
	 */
	
	private $parsed_bit 			= '';	
	
	/**
	 * Zebra in current template?
	 *
	 * @var bool
	 */
	
	private $zebra_in_template 		= false;	
	
	/**
	 * Template contains logic?
	 *
	 * @var bool
	 */
	
	private $logic_in_template	 	= false;	
	
	/**
	 * Parsing error?
	 *
	 * @var bool
	 */
	
	private $parsing_error 			= false;	
	
	/**
	 * Parsing stack
	 *
	 * @var array
	 */
	
	private $parsing_stack 			= array();	
	
	/**
	 * Parsing cursor pos
	 *
	 * @var int
	 */
	
	private $parsing_cursor 		= 0;	
	
	/**
	 * Our template bit functions
	 *
	 * @var array
	 */
	
	private $functions_map 			= array();
	
	// ------------------------------------------------------------------------

	/**
	 * Initialise Compiler
	 *
	 * @param object $appCore
	 * @param integer $style_id
	 * @param array $style_parents
	 * @param string $templates_group
	 */
	
	function __construct( $appCore, $style_id, $style_parents, $templates_group){
		
		// Store appCore pointer
		$this -> appCore = $appCore;
	
		// Store templates group
		$this -> templates_group = $templates_group;
		
		// Select templates
		$appCore -> db -> query( 'SELECT template_name, template_code
		FROM ' . DB_PREFIX . 'styles_templates
		WHERE style_id IN (' . join( ',', $style_parents) . ') AND template_group = \'' . $templates_group . '\'
		ORDER BY FIELD( style_id,' . join( ',', $style_parents) . '), template_name');

		// Put them in our memory
		while ( $template_result = $appCore -> db -> fetch_array())
		{
			if ( !isset( $this -> raw_templates[$template_result[0]]))
			{
				$this -> raw_templates[$template_result[0]] = $template_result[1];
			}
		}
		
		// Free result
		$appCore -> db -> freeResult();
		
	}
	
	// ------------------------------------------------------------------------

	/**
	 * Compiles and saves template to specified file
	 *
	 * @param string $tpl_dir
	 */
	
	function compile( $tpl_dir){
				
		// Start building file
		$ready_file = '<?php

/**
 * ' . PRODUCT_NAME . ' CACHE FILE: COMPILED STYLE TEMPLATES GROUP
 *
 * GENERATED: ' . gmdate( 'r') . '
 */ 

// ------------------------------------------------------------------------
				
class tpl_' . $this -> templates_group . ' extends class_templates_abstract{

	/**
	 * Main class pointer
	 *
	 * @var object
	 */
	
	public $appCore		= NULL;
	
	/**
	 * Template zebras container
	 *
	 * @var array
	 */
	
	public $zebras		= array();

	// ------------------------------------------------------------------------

	/**
	 * Initialise Template
	 *
	 * @param object $appCore
	 */
	
	function __construct( $appCore){
	
		// Store appCore pointer
		$this -> appCore = $appCore;
		
	}
';
		
		// Reiterate our templates
		foreach ( $this -> raw_templates as $template_id => $template_code)
		{
			// Unset our template
			unset( $this -> raw_templates[$template_id]);
			
			// Set logic to false
			$this -> logic_in_template = false;
			
			// Set zebra to false
			$this -> zebra_in_template = false;
			
			// set id
			$this -> actual_template = $template_id;
			
			// add function
			$ready_file .= '	
	// ------------------------------------------------------------------------

	/**
	 * Displays template
	 * ' . $template_id . '
	 *
	 * @param array $template_vars
	 * @return string
	 */
	 	
	function tpl_' . $template_id . '( $template_vars = array()){
	';
		
		// Parsed template
		$parsed_tpl = $this -> parse_template( $this -> fix_slashes( $template_code));
		
		// Does file Contains logic (if/else)?
		if ( $this -> logic_in_template)
		{
			// Use $tpl_html variable
			$ready_file .= '
		$tpl_html = \'' . $parsed_tpl . '\';';
			
			// Update zebras?
			if ( $this -> zebra_in_template)
			{
				$ready_file .= '
				
		$this -> updateZebras( \''.$template_id.'\');';
			}	
			$ready_file .= '
		
		return $tpl_html;';
		}
		else
		{
			// Add simple return
			$ready_file .= '
		return \'' . $parsed_tpl . '\';';
		}
				
			// Close Function
			$ready_file .= '
			
	}
	';
			
		}
		
		// Close class
		$ready_file .= '
}';
		
		// Save class
		file_put_contents( $tpl_dir, $ready_file);
		
	}
	
	// ------------------------------------------------------------------------

	/**
	 * Fixes slashes in text
	 *
	 * @param string $text
	 * @return string
	 */
	
	function fix_slashes( $text){
		
		// Escape slashes
		$text = str_replace( '\\', '\\\\', $text);
		
		// Add slashes
		$text = str_replace( "'", "\'", $text);
				
		// Return ready text
		return $text;
		
	}
	
	// ------------------------------------------------------------------------

	/**
	 * Removes quotes from text
	 *
	 * @param string $text
	 * @return string
	 */
	
	function strip_quotes( $text){
		
		// add slashes
		$text = str_replace( "'", "", $text);
		
		// return ready text
		return $text;
		
	}
	
	// ------------------------------------------------------------------------
	
	/**
	 * Parses template
	 *
	 * @param string $template_code
	 */
	
	function parse_template( $template_code){
		
		// clear stuff
		$this -> remove_slashes = array();
		
		// set code
		$this -> drawed_html = $template_code;
		
		// grab unicodes
		$this -> drawed_html = preg_replace_callback( '#<uni\[(.*?)\]/>#si', array($this, 'parseCodeBit'), $this -> drawed_html);
		
		// return it
		return $this -> drawed_html;
		
	}
	
	// ------------------------------------------------------------------------
	
	/**
	 * Parses code bit
	 *
	 * @param array $matches
	 * @return string
	 */
	
	function parseCodeBit( $matches){

		// Get trimmed code
		$matches[1] = trim( $matches[1]);
				
		// has content?
		if ( isset( $matches[1][0]))
		{
			// Closing?
			if ( $matches[1] == '/if')
			{
				// Close if
				return "';\n\t\t}\n\t\t".'$tpl_html .= \'';
			}
			else
			{
				// Else?
				if ( $matches[1] == 'else')
				{
					return "';\n\t\t}else{\n\t\t".'$tpl_html .= \'';							
				}
				else
				{
					// Code is decising one?
					if ( strpos( $matches[1], ':') !== false)
					{
						// Get bit item to colon?
						$bit_item = substr( $matches[1], 0, strpos( $matches[1], ':'));
						
						// Bit condition
						$bit_condition = $this -> parseCondition( substr( $matches[1], strpos( $matches[1], ':') + 1));
						
						// Handle it specially
						switch ( $bit_item)
						{
							// Open if
							case 'if':
								// Using logic
								$this -> logic_in_template = true;
								
								// Return it
								return "';\n\t\tif(".trim( $bit_condition)."){\n\t\t".'$tpl_html .= \'';
							break;
								
							// Open else
							case 'else if':
							case 'elseif':
								// Return it
								return "';\n\t\t}else if(".trim( $bit_condition)."){\n\t\t".'$tpl_html .= \'';
							break;
						}
					}
					else
					{
						// Well is our bit containing only variable?
						if ( $matches[1][0] == '$' && strlen( $matches[1]) > 1)
						{
							// Simply replace it with variable
							return '\'.( isset( $template_vars[\''.$this -> strip_quotes( substr( $matches[1], 1)).'\']) ? $template_vars[\''.$this -> strip_quotes( substr( $matches[1], 1)).'\'] : null)'.' . \'';	
						}
						else
						{
							// Clear vars
							$this -> parsing_error = false;
							$this -> parsed_bit = $matches[1];
							$this -> parsing_stack = array();
							$this -> parsing_cursor = 0;
							$this -> functions_map = array();
							
							// Fix brackets
							$this -> parsed_bit = str_replace( '{', '[{]', $this -> parsed_bit);
							$this -> parsed_bit = str_replace( '}', '[}]', $this -> parsed_bit);
							
							// There is no other choice than parse item manually one by one
							preg_replace_callback( '#((image|img|language|lang|alink|admin_link|adminlink|nlink|nice_link|nicelink|link|scale|cut|nohtml|urlencode|date|age|number|n|zebra)\()|\)#si', array($this, 'buildBitStack'), $this -> parsed_bit);
							
							// Error encountered?
							if ( $this -> parsing_error)
							{
								return '';
							}
														
							// Set our base html
							$this -> parsed_bit = $this -> parseFunction( $this -> functions_map[(count( $this -> functions_map) - 1)][0], $this -> functions_map[(count( $this -> functions_map) - 1)][1]);
							
							// Remove last function from map
							unset( $this -> functions_map[(count( $this -> functions_map) - 1)]);
							
							// Reverse map
							$this -> functions_map = array_reverse( $this -> functions_map);
							
							// Reiterate rest
							foreach ( $this -> functions_map as $function_id => $function_data)
							{
								// Fix it
								$this -> parsed_bit = str_replace( '{F:'.$function_id.'}', $this -> parseFunction( $function_data[0], $function_data[1]), $this -> parsed_bit);
								
								// Remove from array
								unset( $this -> functions_map[ $function_id]);
							}
								
							// Reverse-fix brackets
							$this -> parsed_bit = str_replace( '[{]', '{', $this -> parsed_bit);
							$this -> parsed_bit = str_replace( '[}]', '}', $this -> parsed_bit);
							
							// Return our function
							return '\' . ( ' . $this -> parsed_bit . ' ) . \'';
						}
					}
				}
			}
		}
				
	}	
		
	// ------------------------------------------------------------------------
	
	/**
	 * Clears and makes safe if condition
	 *
	 * @param string $raw_condition
	 * @return string
	 */
	
	function parseCondition( $raw_condition){

		// Get it
		$this -> parsed_condition = $raw_condition;
		
		// Run parser
		$this -> parsed_condition = preg_replace_callback( '#(\$(_+|[0-9a-zA-Z])+)|\$|(\+|\-|@|\#|%|^|\*|_|\\|/|\?|"|\'|;|:|\.|,|\[|\]|([0-9a-zA-Z]+))+#si', array($this, 'parseConditionItem'), $this -> parsed_condition);
		
		// Reset
		$raw_condition = $this -> parsed_condition;
		
		// Clear cache
		unset( $this -> parsed_condition);
		
		// Return ready condition
		return $raw_condition;
		
	}
	
	// ------------------------------------------------------------------------
	
	/**
	 * Clears template condition item
	 *
	 * @param array $matches
	 * @return string
	 */
	
	function parseConditionItem( $matches){
	
		// Trim
		$matches[0] = trim( $matches[0]);
		
		// Char length
		if ( isset( $matches[0][0]))
		{
			// Is it a var?
			if ( $matches[0][0] == '$')
			{
				// Strlength bigger than 1?
				if( isset( $matches[0][1]))
				{
					// Get key
					$var_key = substr( $matches[0], 1);
					
					// Escape
					return '( isset( $template_vars[\'' . $var_key . '\']) ? $template_vars[\'' . $var_key . '\'] : NULL)';
				}
				else
				{
					// Return escaped
					return "'$'";
				}		
			}
			else
			{
				// special phrase?
				switch ( strtolower( $matches[0]))
				{
					case 'false':
						return 'false';
					break;
					
					case 'true':
						return 'true';
					break;
					
					case 'null':
						return 'NULL';
					break;
					
					default:
						return '\'' . $matches[0] . '\'';
					break;	
				}
			}
		}
		else
		{
			// Return nothing
			return '';
		}
		
	}	
	
	// ------------------------------------------------------------------------
		
	/**
	 * Builds bit stack
	 *
	 * @param array $matches
	 * @return string
	 */
	
	function buildBitStack( $matches){
		
		// Closing stack?
		if ( $matches[0] == ')')
		{
			// Stack has content?
			if ( !empty( $this -> parsing_stack))
			{
				// Get item
				$last_item = array_pop( $this -> parsing_stack);
				
				// Get arguments
				$function_arguments = substr( $this -> parsed_bit, $last_item[1] + strlen( $last_item[0]) + 1);
				$function_arguments = substr( $function_arguments, 0, strpos( $function_arguments, ')'));
				
				// Add it to map
				$this -> functions_map[] = array(
					$last_item[0],
					$function_arguments
				);
				
				// Replace our function inside bit with its key
				$this -> parsed_bit = substr_replace( $this -> parsed_bit, '{F:' . (count( $this -> functions_map) - 1) . '}', $last_item[1], strlen($last_item[0]) + 2 +strlen( $function_arguments));				
			}
			else
			{
				// Something gone wrong, error
				$this -> parsing_error = true;
			}
		}
		else
		{
			// Get item position
			$item_pos = strpos( $this -> parsed_bit, $matches[0], $this -> parsing_cursor);
			
			// Update cursor
			$this -> parsing_cursor = $item_pos;
			
			// Put it onto stack
			$this -> parsing_stack[] = array(
				$matches[2],
				$item_pos
			);		
		}
		
	}

	// ------------------------------------------------------------------------
	
	/**
	 * Converts function from map, into normal function
	 *
	 * @param string $function
	 * @param string $arguments
	 * @return string
	 */
	
	function parseFunction( $function, $arguments){
		
		// Explode arguments
		$arguments = explode( ",", $arguments);
		
		// Arguments exists?
		if ( count( $arguments) > 0)
		{
			// Main argument
			$main_argument = trim( $arguments[0]);
			
			// Fix it
			$main_argument = $this -> clearArgVar( $main_argument);
		}
		else
		{
			// Main argument is null
			$main_argument = 'NULL';
		}
				
		// Switch on function
		switch ( strtolower( $function))
		{
			// Language string
			case 'lang':
			case 'language':
			
				// Remove first argument from array
				unset( $arguments[0]);
				
				// We still have arguments?
				if ( count( $arguments) > 0)
				{
					// Reiterate arguments
					foreach ( $arguments as $arg_id => $arg_var)
					{
						// clear it
						$arg_var = $this -> clearArgVar( $arg_var);
					
						// fix table
						$arguments[$arg_id] = ', ' . $arg_var;
					}
					
					// Return language get string function
					return 'sprintf( $this -> appCore -> lang[' . $main_argument . ']' . join( '', array_reverse( $arguments)) . ')';
				}
				else
				{
					// Return simple string extraction
					return '$this -> appCore -> lang[' . $main_argument . ']';
				}
			
			break;
				
			// Draw style image
			case 'image':
			case 'img':
			
				// Remove first argument from array
				unset( $arguments[0]);
				
				// Array has additional keys?
				while ( count( $arguments) < 4)
				{
					$arguments[] = '';
				}
				
				// Force some types
				settype( $arguments[2], 'integer');
				settype( $arguments[3], 'integer');
				
				// Return image
				return '$this -> appCore -> output -> drawImage( ' . $main_argument . ', ' . $this -> clearArgVar( $arguments[1]) . ', ' . $arguments[2] . ', ' . $arguments[3] . ')';
			
			break;
					
			// Return Nice Link
			// This function gets 4 arguments
			// 0 - QUERY STRING
			// 1 - "SEO TEXT"
			// 3 - ADD KEY-CODE TO URL
			// 4 - USE NICE AMPS
			case 'nlink':
			case 'nicelink':
			case 'nice_link':
				
				// Array has additional keys?
				while ( count( $arguments) < 4)
				{
					$arguments[] = '';
				}
				
				// Parse link argument
				$main_argument = $this -> clearArgUrl( $arguments[0]);
					
				// Force some types
				settype( $arguments[2], 'bool');
				settype( $arguments[3], 'bool');
				
				// Return link
				return '$this -> appCore -> niceLink( ' . $main_argument . ', ' . $this -> clearArgVar( $arguments[1]) . ', ' . $this -> clearArgVar( $arguments[2]) . ', ' . $this -> clearArgVar( $arguments[3]) . ')';

			break;
			
			// Return Admin Link
			// This function gets 3 arguments
			// 0 - QUERY STRING
			// 1 - USE NICE AMPS
			case 'alink':
			case 'adminlink':
			case 'admin_link':	
						
				// Array has additional keys?
				while ( count( $arguments) < 2)
				{
					$arguments[] = '';
				}
				
				// Parse link argument
				$main_argument = $this -> clearArgUrl( $arguments[0]);
					
				// Force some types
				settype( $arguments[1], 'bool');
				
				// Return link
				return '$this -> appCore -> adminLink( ' . $main_argument . ', ' . $this -> clearArgVar( $arguments[1]) . ')';
			
			break;
			
			// Return Basic Link
			// This function gets 3 arguments
			// 0 - QUERY STRING
			// 3 - ADD KEY-CODE TO URL
			// 2 - USE NICE AMPS
			case 'link':	
						
				// Array has additional keys?
				while ( count( $arguments) < 3)
				{
					$arguments[] = '';
				}
				
				// Parse link argument
				$main_argument = $this -> clearArgUrl( $arguments[0]);
					
				// Force some types
				settype( $arguments[1], 'bool');
				settype( $arguments[2], 'bool');
				
				// Return link
				return '$this -> appCore -> link( ' . $main_argument . ', ' . $this -> clearArgVar( $arguments[1]) . ', ' . $this -> clearArgVar( $arguments[2]) . ')';
			
			break;
			
			// Cutted string to specified lenght
			case 'cut':
				
				// Remove first argument from array
				unset( $arguments[0]);
				
				// Cut should take at least one optional parameter (length to cut)
				if ( isset( $arguments[1]))
				{
					// Add dots?
					if ( isset( $arguments[2]) && $arguments[2])
					{
						// Cut with param
						return '$this -> cutString( ' . $main_argument . ', ' . $this -> clearArgVar( $arguments[1]) . ', ' . $this -> clearArgVar( $arguments[2]) . ')';
					}
					else
					{
						// Just cut
						return '$this -> cutString( ' . $main_argument . ', ' . $this -> clearArgVar( $arguments[1]) . ')';
					}
				}
				else
				{
					// Dont cut
					return '$this -> cutString( ' . $main_argument . ')';
				}		
				
			break;
				
			// Formats number
			case 'number':
				
				return '$this -> appCore -> number( ' . $main_argument . ')';
				
			break;
				
			// Draws date
			case 'date':				
				// Make Second Argument string
				$arguments[1] = $this -> clearArgVar(isset( $arguments[1]) ? $arguments[1] : 'long');
				
				// Make Third Argument Bool
				$arguments[2] = (integer) (isset( $arguments[2]) ? $arguments[2] : 0);
				
				// Return string
				return '$this -> appCore -> getDate( ' . $main_argument . ', ' . $arguments[1] . ', ' . $arguments[2] . ')';
				
			break;
				
			// Draws age
			case 'age':				
				
				// Return string
				return '$this -> appCore -> getAge( ' . $main_argument . ')';
				
			break;
				
			// Escapes HTML
			case 'nohtml':
				
				// Remove first argument from array
				unset( $arguments[0]);
				
				// Return slashed text
				return 'htmlspecialchars( ' . $main_argument . ')';
				
			break;
			
			// Encodes URL
			case 'urlencode':
				
				return 'urlencode( ' . $main_argument . ')';
				
			break;
				
			// Scales image down
			case 'scale':
				
				// Array has wanted size?
				if ( count( $arguments) == 4)
				{
					// Return scale
					return '$this -> scaleImage( ' . $this -> clearArgVar( $arguments[0]) . ', ' . $this -> clearArgVar( $arguments[1]) . ', ' . $this -> clearArgVar( $arguments[2]) . ', ' . $this -> clearArgVar( $arguments[3]) . ')';
				}
				else
				{
					// Return empty scale
					return '$this -> scaleImage( 0, 0, 0, 0)';
				}
				
			break;
			
			// Zebra
			case 'zebra':
				
				// Tell parser we have zebra, so we have to use logic
				$this -> zebra_in_template = true;
				$this -> logic_in_template = true;
				
				// Remove first argument from array
				unset( $arguments[0]);
				
				// Get basic args
				$zebra_template = "'".$this -> actual_template."'";
				$zebra_group = "''";
				$zebra_jump = "''";
				
				// Reiterate arguments
				foreach ( $arguments as $raw_argument)
				{
					// Get arg name?
					if ( strpos( $raw_argument, "=") > 0)
					{
						// Get argument name
						switch ( strtolower( substr( $raw_argument, 0, strpos( $raw_argument, '='))))
						{
							case 'group':
								$zebra_group = $this -> clearArgVar( substr( $raw_argument, strpos( $raw_argument, '=') + 1));	
							break;
							
							case 'jump':
								$zebra_jump = $this -> clearArgVar( substr( $raw_argument, strpos( $raw_argument, '=') + 1));		
							break;							
						}
					}
				}
				
				// Return function
				return '$this -> zebra( ' . $zebra_template . ', ' . $main_argument . ', ' . $zebra_group . ', ' . $zebra_jump . ')';
			
			break;
		}
		
	}
	
	// ------------------------------------------------------------------------
	
	/**
	 * Clears argument var
	 *
	 * @param string $var
	 * @return string
	 */
	
	function clearArgVar( $var){
		
		// Trim var
		$var = trim( $var);
		
		// Var has value?
		if ( isset( $var[0]))
		{
			// Is it a variable?
			if ( isset( $var[1]) && $var[0] == '$')
			{
				// Return variable
				return '( isset( $template_vars[\'' . substr( $var, 1) . '\']) ? $template_vars[\'' . substr( $var, 1) . '\'] : NULL)';
			}
			else
			{
				// Is it a function?
				if ( $var[0] == '{')
				{
					return $var;	
				}
				else
				{
					// Its forced?
					switch ( strtolower( $var))
					{
						case 'false':
							return 'false';
						break;
						
						case 'true':
							return 'true';
						break;
						
						case 'null':
							return 'NULL';
						break;
						
						// Defaultly its a string
						default:
							return '\'' . $var . '\'';
						break;					
					}
				}				
			}
		}
		else
		{	
			// Return empty string
			return '\'\''; 	
		}
		
	}
	
	// ------------------------------------------------------------------------
	
	/**
	 * Clears argument for URL
	 *
	 * @param string $var
	 * @return string
	 */
	
	function clearArgUrl( $var){
		
		// Explode query string
		$query_items = explode( '&', $var);
		
		// Ready string
		$ready_string = '';
		
		// In string?
		$in_string = false;
		
		// Reiterate query items
		foreach ( $query_items as $item_raw)
		{
			// Item name
			$item_name = substr( $item_raw, 0, strpos( $item_raw, '='));
			
			// Item value
			$item_value = substr( $item_raw, strpos( $item_raw, '=') + 1);
			
			// Both thingies are not empty?
			if ( isset( $item_name[0]) && isset( $item_value[0]))
			{
				// Are we not in string?
				if ( !$in_string)
				{
					// Add single quote
					$ready_string .= '\'';
					
					// Change string state
					$in_string = true;
				}
				
				// Add AMP?
				if ( isset( $ready_string[2]))
				{
					$ready_string .= '&';
				}
				
				// Add item name
				$ready_string .= $item_name . '=';
				
				// Secure item value
				$item_value = $this -> clearArgVar( $item_value);
				
				// Item is raw-string?
				if( $item_value[0] == '\'')
				{
					// Add item without slash at beginning and end
					$ready_string .= substr( $item_value, 1,  strlen( $item_value) - 2);
				}
				else
				{
					// If we are in string, we gonna leave it
					if ( $in_string)
					{
						// Add single quote
						$ready_string .= '\' . ' . $item_value;
						
						// Change string state
						$in_string = false;
					}
				}
			}
		}
		
		// Close string?
		if ( $in_string)
		{
			$ready_string .= '\'';
		}
				
		// Return joined query items OR NULL
		return ( isset( $ready_string[0]) ? $ready_string : 'NULL');
		
	}
	
}